﻿using Basic.ScheduledTask;
using Basic.ScheduledTask.Abstract;
using Basic.ScheduledTask.HangfireImplement;
using FluentScheduler;
using Hangfire;
using Hangfire.Dashboard;
using Hangfire.MemoryStorage;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

namespace System;

public static class JobExtension
{

    /// <summary>
    /// 注册定时任务
    /// </summary>
    /// <param name="services"></param>
    /// <param name="assemblies"></param>
    /// <returns></returns>
    public static IServiceCollection AddScheduledJob(this IServiceCollection services, params Assembly[] assemblies)
    {
        Type interfaceType = typeof(IScheduledJob);

        if (assemblies?.Length > 0)
            foreach (var assembly in assemblies)
            {
                var jobTypes = assembly.GetExportedTypes().Where(s => s.GetInterface(interfaceType.Name) != null).ToList();
                foreach (var jobType in jobTypes)
                {
                    services.AddTransient(jobType);
                    services.AddTransient(interfaceType, jobType);
                }
            }

        return services;
    }

    /// <summary>
    /// 启动定时任务
    /// </summary>
    /// <param name="app"></param>
    /// <returns></returns>
    public static IApplicationBuilder UseScheduledJob(this IApplicationBuilder app)
    {
        var jobManager = app.ApplicationServices.GetService<IJobManager>();
        var jobs = app.ApplicationServices.GetServices<IScheduledJob>();
        foreach (var job in jobs)
        {
            jobManager.AddRecurringJob(job.GetType().GUID.ToString(), job);
        }
        return app;
    }

    /// <summary>
    /// 添加定时任务管理器
    /// </summary>
    /// <param name="services"></param>
    /// <param name="section"></param>
    /// <returns></returns>
    public static IServiceCollection AddHangfireJobManager(this IServiceCollection services, IConfigurationSection section)
    {
        services.Configure<JobDbConfig>(section);
        services.AddSingleton<IJobManager, HangfireJobManager>();
        var conf = section.Get<JobDbConfig>();
        string dbType = conf?.DbType?.ToLower();
        if (string.Equals(dbType, "sqlserver") || string.Equals(dbType, "mssql"))
            services.AddHangfire(configuration => configuration
                .SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
                .UseSimpleAssemblyNameTypeSerializer()
                .UseRecommendedSerializerSettings()
                .UseSqlServerStorage(conf.ConnetionString));
        //else if (string.Equals(dbType, "postgresql"))
        //    services.AddHangfire(configuration => configuration
        //        .SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
        //        .UseSimpleAssemblyNameTypeSerializer()
        //        .UseRecommendedSerializerSettings()
        //        .UsePostgreSqlStorage(pg =>
        //        {
        //            pg.UseNpgsqlConnection(conf.ConnetionString);
        //        }));
        else
            services.AddHangfire(configuration => configuration
                .SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
                .UseSimpleAssemblyNameTypeSerializer()
                .UseRecommendedSerializerSettings()
                .UseMemoryStorage(new MemoryStorageOptions
                {
                    JobExpirationCheckInterval = TimeSpan.FromMinutes(30),
                    CountersAggregateInterval = TimeSpan.FromMinutes(3.0),
                    FetchNextJobTimeout = TimeSpan.FromMinutes(30.0)
                }, new Hangfire.MemoryStorage.Database.Data()));

        services.AddHangfireServer();

        return services;
    }

    public static IApplicationBuilder UseHangfireJobManager(this IApplicationBuilder app)
    {
        app.UseHangfireDashboard("/hangfire", new DashboardOptions
        {
            IsReadOnlyFunc = (DashboardContext context) => true
        });
        return app;
    }


    #region FluentScheduler注册器

    /// <summary>
    /// 注入FluentScheduler，配合UseFluentScheduler()使用
    /// </summary>
    /// <param name="services"></param>
    /// <param name="assemblies"></param>
    public static IServiceCollection AddFluentSchedulerSetup(this IServiceCollection services, params Assembly[] assemblies)
    {
        List<Type> registryTypes = new List<Type>();
        List<Type> jobTypes = new List<Type>();

        Type baseType = typeof(Registry);
        Type jobType = typeof(IJob);

        //程序集中实现了Registry的类
        foreach (var assembly in assemblies)
        {
            var collection = assembly.GetTypes().Where(t => !t.IsInterface && !t.IsAbstract);

            var registrys = collection.Where(t => t.BaseType?.GUID == baseType.GUID).ToArray();
            if (registrys?.Length > 0) registryTypes.AddRange(registrys);

            var jobList = collection.Where(s => s.GetInterfaces()?.Contains(jobType) == true).ToArray();
            if (jobList.Length > 0) jobTypes.AddRange(jobList);
        }
        // 注册IJob,
        foreach (var type in registryTypes)
            services.AddSingleton(baseType, type);

        // IJob
        foreach (var type in jobTypes)
            services.AddSingleton(type);

        return services;
    }

    /// <summary>
    /// 注册FluentSchedulerJobFactory
    /// </summary>
    /// <param name="app"></param>
    /// <returns></returns>
    public static IApplicationBuilder UseFluentScheduler(this IApplicationBuilder app)
    {
        FluentSchedulerJobFactory factory = new FluentSchedulerJobFactory(app.ApplicationServices);
        JobManager.JobFactory = factory;

        Registry[]? registryList = app.ApplicationServices.GetServices<Registry>()?.ToArray();
        JobManager.Initialize(registryList);

        return app;
    }

    #endregion
}